package server

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-clog/clog"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/natefinch/lumberjack"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/xiaobaiskill/fang/internal/proxypool"
	"github.com/xiaobaiskill/fang/internal/proxypool/publicproxy"
	fang365 "github.com/xiaobaiskill/fang/internal/task/request/365"
	"github.com/xiaobaiskill/fang/internal/websocket"
	"github.com/xiaobaiskill/fang/pkg/conf"
	"github.com/xiaobaiskill/fang/pkg/log"
	"github.com/xiaobaiskill/fang/pkg/models"
	"github.com/xiaobaiskill/fang/pkg/pool"
	"github.com/xiaobaiskill/fang/pkg/redis"
	"github.com/xiaobaiskill/fang/routes"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	mysql_log "log"
	"net/http"
	"os"
	"path"
	"time"
)

type Server struct {
	cfg        *conf.Config
	done       chan bool
	proxypools *proxypool.Register
}

func New(cfg *conf.Config) *Server {
	s := &Server{cfg: cfg, done: make(chan bool)}
	s.configureLog()

	s.newDB()
	s.NewRedis()

	s.Proxypools()
	s.request()

	return s
}

// 日志配置
func (s *Server) configureLog() {
	levelNames := map[string]zapcore.Level{
		"info":  zapcore.InfoLevel,
		"warn":  zapcore.WarnLevel,
		"error": zapcore.ErrorLevel,
		"fatal": zapcore.FatalLevel,
	}

	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		level, ok := levelNames[s.cfg.LogFile.Level]
		if ok {
			return lvl >= level
		}
		return lvl >= zapcore.ErrorLevel
	})
	hook := lumberjack.Logger{
		Filename:   path.Join(s.cfg.Log.RootPath, "log.txt"),
		MaxSize:    1 << s.cfg.LogFile.MaxSizeShift, // megabytes
		MaxBackups: s.cfg.LogFile.MaxBackups,
		MaxAge:     s.cfg.LogFile.MaxDays,  //days
		Compress:   s.cfg.LogFile.Compress, // disabled by default
	}
	fileWriter := zapcore.AddSync(&hook)
	consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())

	var allCore []zapcore.Core
	if s.cfg.Debug {
		consoleDebugging := zapcore.Lock(os.Stdout)
		lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
			level, ok := levelNames[s.cfg.LogConsole.Level]
			if ok {
				return lvl >= level
			}
			return lvl >= zapcore.DebugLevel
		})
		allCore = append(allCore, zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority))
	}
	allCore = append(allCore, zapcore.NewCore(consoleEncoder, fileWriter, highPriority))

	core := zapcore.NewTee(allCore...)
	log.InitLog(core, s.cfg.AppName)
	log.Logger.Info("程序日志启动。。。")
}

func (s *Server) newDB() {
	g, err := gorm.Open(s.cfg.Database.Driver, s.cfg.Database.DataSource)

	if err != nil {
		panic("数据库链接失败")
	}

	// configure xorm
	err = s.configureGORM(g)
	if err != nil {
		panic("数据库配置初始化失败")
	}
	models.Conn = g

}

func (s *Server) NewRedis() {
	conn := redis.New(s.cfg.Redis.Addr)
	if _, err := conn.Ping(); err != nil {
		panic("redis 链接失败")
	}
	redis.RedisConn = redis.NewRedisDB(conn)
}

// db 的配置
func (s *Server) configureGORM(g *gorm.DB) error {
	logger, err := clog.NewFileWriter(path.Join(s.cfg.Log.RootPath, "gorm.log"),
		clog.FileRotationConfig{
			Rotate:  s.cfg.LogXorm.Rotate,
			Daily:   s.cfg.LogXorm.RotateDaily,
			MaxSize: s.cfg.LogXorm.MaxSize * 1024 * 1024,
			MaxDays: s.cfg.LogXorm.MaxDays,
		})
	if err != nil {
		return fmt.Errorf("Fail to create 'xorm.log': %s", err)
	}

	g.SetLogger(mysql_log.New(logger, "\r\n", 0))

	//g.AutoMigrate(models.NewIP())
	g.AutoMigrate(models.NewFangInfo())
	return nil
}

func (s *Server) Proxypools() {
	r := proxypool.NewRegister()
	//fmt.Println(s.cfg.Proxypool)
	r.Add(publicproxy.NewPublicProxy(s.cfg.Proxypool.PublicproxyMinsize), s.cfg.Proxypool.PublicproxyRetryNum)
	//r.Add(backupproxy.NewBackupProxy(s.cfg.Proxypool.BackupproxyConf, s.cfg.Pool.WorkSize), s.cfg.Proxypool.BackupproxyRetryNum)
	//r.Add(selfproxy.NewSelf(s.cfg.Proxypool.SelfUrl), s.cfg.Proxypool.SelfRetryNum)
	s.proxypools = r
}

func (s *Server) request() {
	fang365.Newfang365()
	go func() {
		tc := time.NewTicker(time.Second * 3)
		var nhour int
		for {
			select {
			case <-tc.C:
				nhour = time.Now().Hour()
				if nhour > 9 && nhour < 21 {
					pool.WorkQueue <- fang365.NewFangInfo("nj", 20)
				}
			}
		}
	}()
}

func (s *Server) Run() {
	router := gin.New()
	routes.InitRouter(router)

	router.GET("/workpool_metrics", pool.StartDispathcher(s.cfg.Pool.WorkSize))
	router.GET("/proxypool_metrics", s.proxypools.Metrics.GinHandler())
	router.GET("/metrics", func() gin.HandlerFunc {
		return func(c *gin.Context) {
			promhttp.Handler().ServeHTTP(c.Writer, c.Request)
		}
	}())

	router.LoadHTMLGlob("views/**/*")
	router.StaticFS("/public", http.Dir("static/"))

	// 开启websocket
	router.GET("/ws", websocket.Ws())
	router.Run(fmt.Sprintf("%s:%d", s.cfg.Server.HttpAddr, s.cfg.Server.HttpPort))
}

func (s *Server) Stop() {
	pool.StopDispathcher()
}
